package com.dayuanit.booksystem.v2.dao.impl;

import com.dayuanit.booksystem.v2.domain.PrepareParamer;
import com.dayuanit.booksystem.v2.thread.ConnectionThread;
import com.dayuanit.booksystem.v2.util.DataBaseUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class BaseDaoImpl {

    protected static final Map<String, String> bookMap = new HashMap<>();
    protected static final Map<String, String> publisherMap = new HashMap<>();

    static {
        bookMap.put("id", "book_id");
        bookMap.put("bookName", "book_name");
        bookMap.put("author", "author");
        bookMap.put("price", "price");
        bookMap.put("publisherId", "publisher_id");

        publisherMap.put("id", "publisher_id");
        publisherMap.put("name", "publisher_name");
        publisherMap.put("address", "publisher_address");
    }

    /**
     * 统一添加  通过PreparedStatement获取自增主键
     * @param sql
     * @return
     */
    protected static long commonAdd(String sql, List<PrepareParamer> prepareParamersList) {
        System.out.println(sql);
        int rows = -1;

        //从当前线程获取Connection
        Connection connection = ConnectionThread.connThreadLocal.get();
        System.out.println(">>>>>>>DAO:" + connection.toString());

        try (PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {

            for (int index = 0; index<prepareParamersList.size(); index ++) {
                PrepareParamer prepareParamer = prepareParamersList.get(index);

                if (prepareParamer.getParamerClassType() == String.class) {
                    preparedStatement.setString(index + 1, (String)prepareParamer.getValue());
                }

                if (prepareParamer.getParamerClassType() == long.class) {
                    preparedStatement.setLong(index + 1, (long)prepareParamer.getValue());
                }
                //....
            }

            rows = preparedStatement.executeUpdate();
            System.out.println("影响行数:" + rows);
            ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
            if (generatedKeys.next()) {
                long id = generatedKeys.getLong(1);
                System.out.println("自增ID:" + id);
                return id;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return -1;
    }

    /**
     * 统一修改语句  修改和删除
     *
     * Statement是SQL拼接的，很容易进行SQL注入的攻击，
     * 解决方案，用PrepareStamtent代替Statement
     * @param sql
     */
    protected static int modify(String sql, List<PrepareParamer> prepareParamersList) {
        System.out.println(sql);
        int rows = -1;
        Connection connection = ConnectionThread.connThreadLocal.get();

        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            //执行之前要填充占位符  UPDATE book set price=? where book_name=?
            for (int index = 0; index<prepareParamersList.size(); index++) {
                PrepareParamer prepareParamer = prepareParamersList.get(index);
                if (prepareParamer.getParamerClassType() == String.class) {
                    preparedStatement.setString(index + 1, (String)prepareParamer.getValue());
                }

                if (prepareParamer.getParamerClassType() == long.class) {
                    preparedStatement.setLong(index + 1, (long)prepareParamer.getValue());
                }

                //.... 8个基本类型
            }
            rows = preparedStatement.executeUpdate();
            System.out.println("影响行数:" + rows);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rows;
    }

    protected static <T> List<T> commonQueryList(String sql, Class<T> claz, Map<String, String> fieldMap, List<PrepareParamer> prepareParamersList) {
        System.out.println(sql);
        //此集合是用来存放查询出来的对象
        List<T> list = new ArrayList<>();

        //得到类的所有成员变量
        Field[] fields = claz.getDeclaredFields();

        //从当前线程里得到Connection
        Connection connection = ConnectionThread.connThreadLocal.get();

        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {

            //填充sql语句？占位符
            for (int index = 0; index<prepareParamersList.size(); index++) {
                PrepareParamer prepareParamer = prepareParamersList.get(index);
                if (prepareParamer.getParamerClassType() == String.class) {
                    preparedStatement.setString(index + 1, (String)prepareParamer.getValue());
                }

                if (prepareParamer.getParamerClassType() == long.class) {
                    preparedStatement.setLong(index + 1, (long)prepareParamer.getValue());
                }

                //.... 8个基本类型
            }

            ResultSet rs = preparedStatement.executeQuery();
            //循环结果集的每一行
            while (rs.next()) {
                T t = claz.newInstance();

                //循环当前行的每一列 for循环结束后，那么对象t的成员变量就有值了
                for (Field field : fields) {
                    if (field.getType() == String.class) {
                        //当前列的值
                        String value = rs.getString(fieldMap.get(field.getName()));

                        //找到当前属性的对应的set方法，通过反射调用之
                        Method method = claz.getMethod("set" + format(field.getName()), String.class);
                        method.invoke(t, value);//t.setId(value);
                    }

                    if (field.getType() == long.class) {
                        //当前列的值
                        long value = rs.getLong(fieldMap.get(field.getName()));

                        //找到当前属性的对应的set方法，通过反射调用之
                        Method method = claz.getMethod("set" + format(field.getName()), long.class);
                        method.invoke(t, value);//t.setId(value);
                    }

                    //TODO .... 正常情况下 应该有9个if判断，八个基本类型 和 String
                }

                //将本次循环的结果产生的对象t放到集合中
                list.add(t);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    protected static <T> T commonQuery(String sql, Class<T> claz, Map<String, String> fieldMap, List<PrepareParamer> prepareParamerList) {
        List<T> list = commonQueryList(sql, claz, fieldMap, prepareParamerList);
        if (!list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    public static int commonCount(String sql,  List<PrepareParamer> prepareParamerList) {
        System.out.println(sql);

        //从当前线程里得到Connection
        Connection connection = ConnectionThread.connThreadLocal.get();

        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {

            //填充sql语句？占位符
            for (int index = 0; index<prepareParamerList.size(); index++) {
                PrepareParamer prepareParamer = prepareParamerList.get(index);
                if (prepareParamer.getParamerClassType() == String.class) {
                    preparedStatement.setString(index + 1, (String)prepareParamer.getValue());
                }

                if (prepareParamer.getParamerClassType() == long.class) {
                    preparedStatement.setLong(index + 1, (long)prepareParamer.getValue());
                }

                //.... 8个基本类型
            }

            ResultSet rs = preparedStatement.executeQuery();
            if (rs.next()) {
                return rs.getInt(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return -1;
    }

    private static String format(String fieldName) {
        char[] chars = fieldName.toCharArray();
        chars[0] = String.valueOf(chars[0]).toUpperCase().toCharArray()[0];
        return new String(chars);
    }
}
